import { markRaw, type Component } from 'vue'
import { nextKey, type ComponentOptions } from '@/widgets'
import type { MapFeatures } from '@/map'
import { registerIndex, type MapItemIndex } from './map-index'
import type { MapItemInfo, MapItemTypeInfo } from './types'

export type MapItemHighlight<T> = (e: T) => MapFeatures | null | undefined

export interface MapItemOptions<T> {
  type: string
  typeName: string
  icon?: string
  getName(t: T): string
  index?: MapItemIndex<T>
  highlight?: MapItemHighlight<T>
  infoView?: Component | ((t: T) => Component | null)
}

export type MapItemInfoView<T> = (t: MapItemInfo<T>) => Required<ComponentOptions> | null
export interface MapItemTypeSpec<T> {
  type: string
  typeName: string
  icon?: string
  getName(t: T): string
  highlight?: MapItemHighlight<T>
  infoViews: MapItemInfoView<T>[]
}

const mapItemSpecs: Record<string, MapItemTypeSpec<any>> = {}

export function registerMapItem<T>(opts: MapItemOptions<T>) {
  const { type, typeName, icon, index, getName, highlight, infoView } = opts
  const spec = {
    type,
    typeName,
    icon,
    getName,
    highlight,
    infoViews: infoView ? [wrap(infoView)] : []
  }
  mapItemSpecs[type] = spec
  if (index) {
    registerIndex(type, index)
  }
}

export function addMapItemInfoView<T>(
  type: string,
  infoView: Component | ((t: T) => Component | null)
) {
  const spec = mapItemSpecs[type]
  if (!spec) {
    return
  }
  spec.infoViews.push(wrap(infoView))
}

function wrap<T>(infoView: Component | ((t: T) => Component | null)): MapItemInfoView<T> {
  let fn: (t: T) => Component | null
  if (typeof infoView === 'function') {
    fn = infoView as (t: T) => Component | null
  } else {
    fn = (t: T) => infoView
  }
  return (t: MapItemInfo<T>) => {
    const iv = fn(t.item)
    if (!iv) {
      return null
    }
    return {
      key: nextKey(),
      component: iv,
      props: {
        type: t.type,
        typeName: t.typeName,
        item: markRaw(t.item as object)
      },
      events: {}
    }
  }
}

export function makeHighlightMapItemFeatures(type: string, e: any): MapFeatures | undefined | null {
  const opts = mapItemSpecs[type]
  if (!opts || !opts.highlight) {
    return undefined
  }
  return opts.highlight(e)
}

export function getMapItemTypeSpec<T>(type: string): MapItemTypeSpec<T> | undefined {
  const opts = mapItemSpecs[type]
  if (!opts) {
    return undefined
  }
  return opts
}

export function getMapItemInfoViews(item: MapItemInfo<any>): Required<ComponentOptions>[] {
  const infoViews = mapItemSpecs[item.type].infoViews
  if (!infoViews || infoViews.length === 0) {
    return []
  }
  const ret = []
  for (const iv of infoViews) {
    const e = iv(item)
    if (e) {
      ret.push(e)
    }
  }
  return ret
}

export function getMapItemTypeInfo(type: string): MapItemTypeInfo | undefined {
  return getMapItemTypeSpec(type)
}
